// OpentTxl-C Version 11 safe C try/catch exception handling
// J.R. Cordy, Jan 2023

// Copyright 2023, James R. Cordy and others

// Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
// and associated documentation files (the “Software”), to deal in the Software without restriction, 
// including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all copies 
// or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 
// AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// OpenTxl safe C try/catch exception handling 

// Example:
//      try {
//          ...
//          throw (99);
//          ...
//      } catch {
//          if (exception = 99 ) {
//              fprintf (stderr, "caught signal %d\n", exception);
//          }
//          throw (exception);
//      }

// Standard libraries
#include <stdio.h>
#include <setjmp.h>

// Check interface consistency
#include "trycatch.h"

// Active, current and default catch handlers
struct trycatch_handlerT *trycatch_activehandler;
struct trycatch_handlerT *trycatch_currenthandler;
struct trycatch_handlerT trycatch_defaulthandler;

// If we're using BSD-style signals, we need this
#ifdef BSD
    #include <signal.h>
#endif

static void trycatch_reverthandlers (void)
{
    // revert to system default signal handlers 
#ifdef BSD
    if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
        (void) signal(SIGINT, SIG_DFL);
    }
    if (signal(SIGILL, SIG_IGN) != SIG_IGN) {
        (void) signal(SIGILL, SIG_DFL);
    }
    if (signal(SIGFPE, SIG_IGN) != SIG_IGN) {
        (void) signal(SIGFPE, SIG_DFL);
    }
    #ifdef SIGBUS
        if (signal(SIGBUS, SIG_IGN) != SIG_IGN) {
            (void) signal(SIGBUS, SIG_DFL);
        }
    #endif
    if (signal(SIGSEGV, SIG_IGN) != SIG_IGN) {
        (void) signal(SIGSEGV, SIG_DFL);
    }
#endif
}

static void trycatch_globalhandler (int signalNo)
{
    /* revert to default signal handlers */

    trycatch_reverthandlers ();

    /* print the appropriate message */
#ifdef BSD
    /* Version using BSD signals */

    switch (signalNo) {
        case SIGINT:
            fputs ("Program terminated\n", stderr);
            break;
        case SIGILL:
            fputs ("Illegal instruction\n", stderr);
            break;
        case SIGFPE:
            fputs ("Floating point exception\n", stderr);
            break;
#ifdef SIGBUS
        case SIGBUS:
            fputs ("Bus error\n", stderr);
            break;
#endif
        case SIGSEGV:
            fputs ("Segmentation violation\n", stderr);
            break;
        default:
            fprintf (stderr, "TXL ERROR: Unexpected signal %d\n", signalNo);
            break;
    }

    /* call the Turing handler */
    trycatch_currenthandler = trycatch_activehandler;
    trycatch_currenthandler->code = signalNo;
    if (trycatch_currenthandler != &trycatch_defaulthandler) 
        trycatch_activehandler = trycatch_currenthandler->prevhandler; 
    longjmp (trycatch_currenthandler->environment, signalNo);
#endif
}

static void trycatch_initializehandlers (void)
{
#ifdef BSD
    /* Version using BSD signals */

    if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
        (void) signal(SIGINT, trycatch_globalhandler);
    }
    (void) signal(SIGILL, trycatch_globalhandler);
    (void) signal(SIGFPE, trycatch_globalhandler);
#ifdef SIGBUS
    (void) signal(SIGBUS, trycatch_globalhandler);
#endif
    (void) signal(SIGSEGV, trycatch_globalhandler);
#endif
}

void tcinitialize (int argc, char **argv)
{
    trycatch_activehandler = &trycatch_defaulthandler;
    trycatch_activehandler->prevhandler = (struct trycatch_handlerT *) 0;
    trycatch_activehandler->code = 0;
    trycatch_initializehandlers ();
}

void tcfinalize (void)
{
    trycatch_reverthandlers ();
}
